package com.gateway.security.authentication.manager;

import com.gateway.common.enums.AuthenticationTypeEnum;
import com.gateway.security.authentication.client.ClientPrincipalConverterFactory;
import com.gateway.security.common.authentication.client.ClientAuthenticationToken;
import com.gateway.security.common.exception.TokenAuthenticationException;
import com.gateway.security.endpoint.TokenEndpointService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.core.Authentication;
import reactor.core.publisher.Mono;

/**
 * @program: api-gateway
 * @description:
 * @author: YuKai Fan
 * @create: 2025/2/10 23:41
 **/
public class SecurityReactiveAuthenticationManager implements ReactiveAuthenticationManager {
    private static final Logger log = LoggerFactory.getLogger(SecurityReactiveAuthenticationManager.class);

    private final TokenEndpointService tokenEndpointService;

    public SecurityReactiveAuthenticationManager(TokenEndpointService tokenEndpointService) {
        this.tokenEndpointService = tokenEndpointService;
    }

    @Override
    public Mono<Authentication> authenticate(Authentication authentication) {
        return Mono.justOrEmpty(authentication)
                .filter(ClientAuthenticationToken.class::isInstance)
                .cast(ClientAuthenticationToken.class)
                .flatMap(this::authenticate);
    }

    private Mono<Authentication> authenticate(ClientAuthenticationToken authenticationToken) {
        AuthenticationTypeEnum authenticationType = authenticationToken.getApiDefinition().getAuthenticationType();
        log.info("SecurityReactiveAuthenticationManager[authenticate] start authentication by authType is {}", authenticationToken.getApiDefinition().getAuthorizationType());
        return tokenEndpointService.getClaims(authenticationToken)
                .switchIfEmpty(Mono.error(new TokenAuthenticationException("认证失败")))
                .flatMap(claims -> ClientPrincipalConverterFactory.convert(authenticationType.getType(), claims))
                .flatMap(principal -> Mono.justOrEmpty(new ClientAuthenticationToken(principal, authenticationToken.getToken(), authenticationToken.getApiDefinition())));
    }
}
